package com.example.codegen;

import com.example.utils.Utils;

import java.io.IOException;
import java.util.Arrays;
import java.util.Optional;
import java.util.ArrayList;
import java.io.BufferedReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

// out ebnf looks like
//// 标识符 非数字开头的字母和数字组合 如a1
//identifier = alphabetic character , [ { alphabetic character | digit } ] ;
//// 数字 如12
//        number = [ "-" ] , digit , [ { digit } ] ;
//// 基础数据类型
//        primitiveType = "byte" | "char" | "boolean" | "short" | "int" | "float" | "double" | "long" | "void";
//        dimension, index, length = number;
//        className, methodName, parameter = identifier;
//// 类型由基础类型、class类型， 及数组组合而成 如 student[][], int[]
//        type = (primitiveType | className) , { "[", "]" };
//// 数组变量可以通过数组变量名和下标进行访问 如stu[11]
//        arrayVariable = identifier, "[", index , "]";
//// 实例变量或类变量通过名称访问 如 a
//        instanceVariable = identifier;
//// 变量可以由数组变量和类变量等通过"."连接 如 stu[11].course
//        variable = [ "(", type, ")" ] , ( arrayVariable | instanceVariable ) , { "." , ( arrayVariable | instanceVariable) } ;
//// 方法调用由方法名和参数构成，保证参数都为identifier不包含复杂表达式 如func(a,b)
//        callExpr = methodName , "( ", { parameter }, ")";
//// 如 NewInstance(Student)
//        newInstanceExpr = "NewInstance", "(" , className, ")";
//// 如 NewArray(Student, 2, 3)
//        newArrayExpr = "NewArray" , "(", type, dimension, length, ")";
//// 定义，包括类型和变量名 如 Student a;
//        declareExpr = type， identifier；
//        // 新建对象， NewInstance或NewArray
//        newStmt = newInstanceExpr | newArrayExpr;
//// 更新操作即AssignStatement 左值可以为声明变量或者声明， 右值可以为新建对象、函数调用和变量， 如refUpdate(Student a, NewInstance(Student))
//        refUpdateStmt = "refUpdate", "(", (variable | declareExpr) , (newInstanceExpr | newArrayExpr | callExpr | variable) , ")";
//// return 保证没有不返回函数调用 如 return a.b;
//        returnStmt = "return" , variable;
//        callStmt = callExpr;
//// 基础语句包括对象更新、函数调用、返回；
//        stmt = refUpdateStmt | returnStmt | callStmt;
//// 函数由返回值、方法名称、 参数类型、参数名称、 函数语句等构成。
//        method = type , methodName, "(", { type, parameter}, ")" , "{", {stmt}, "}";


public class Parser {

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }

    //    // 类型由基础类型、class类型， 及数组组合而成   如 student[][], int[]
//    type = (primitiveType | className) , { "[", "]" };
    public static Optional<Type> parserType(String line) {
        String[] strs = line.split(" ");
        if (strs.length != 1) {
            return Optional.empty();
        }
        Optional<String[]> matchStr = Utils.RegularExpressionGroups(strs[0], String.format("(%s)((\\[\\])*)", Identifier.IDENTIFIER_PATTERN));
        if (matchStr.isEmpty()) {
            return Optional.empty();
        }
        String typeName = matchStr.get()[1];
        int dimensions = (matchStr.get()[2].length()) / 2;
        return Optional.of(new Type(typeName, dimensions));
    }

    //    // 数组变量可以通过数组变量名和下标进行访问 如stu[11]， aa[1][2];
    //    arrayVariable = identifier ("[" index "]")*;
//// 实例变量或类变量通过名称访问 如 a
//    instanceVariable = identifier | constant;
    public static Optional<Variable> parserVariable(String line) {
        line = line.strip();
        if (line.split(" ").length != 1) {
            return Optional.empty();
        }
        String[] strs = line.split("(\\[|\\])");
        if (strs.length == 0) {
            return Optional.empty();
        }
        Optional<String[]> identifier = Utils.RegularExpressionGroups(strs[0], Identifier.IDENTIFIER_PATTERN);
        if (identifier.isEmpty()) {
            return Optional.empty();
        }
        Optional<String[]> indexes = strs.length > 1 ? Optional.of(Arrays.copyOfRange(strs, 1, strs.length)) : Optional.empty();
        return Optional.of(new Variable(strs[0], indexes));
    }

    //    // 变量可以由数组变量和类变量等通过"."连接 如 stu[11].course
//    variable = [ "(", type, ")" ] , ( arrayVariable | instanceVariable ) , { "." ,  ( arrayVariable | instanceVariable)  } ;
    public static Optional<Variables> parserVariables(String line) {
        String l = line.strip();
        Optional<Type> type = Optional.empty();
        // parse type , check if there is (type)
        Optional<String[]> matchStr = Utils.RegularExpressionGroups(l, String.format("\\((.*?)\\)(.*?)"));
        if (matchStr.isPresent()) {
            Optional<Type> t = parserType(matchStr.get()[1]);
            if (t.isEmpty()) {
                return Optional.empty();
            }
            type = t;
            l = matchStr.get()[2].strip();
        }
        // remove the outer () if exists
        if (l.startsWith("(") && l.endsWith(")")) {
            l = l.substring(1, l.length() - 1);
        }
        String[] strs = l.strip().split(" ");
        if (strs.length != 1) {
            return Optional.empty();
        }
        strs = l.split("\\.");
        if (!(strs.length > 0)) {
            return Optional.empty();
        }
        ArrayList<Variable> variables = new ArrayList<>();
        for (String str : strs) {
            Optional<Variable> variable = parserVariable(str);
            if (variable.isEmpty()) {
                return Optional.empty();
            }
            variables.add(variable.get());
        }
        return Optional.of(new Variables(type, variables));
    }

    //    // 实例变量类型, 如 static String str
//    field = ["static "], type,  " ", identifier;
    public static Optional<Field> parserField(String line, String className) {
        String[] strs = line.strip().split("[;\\s]+");
        if (strs.length != 3 && strs.length != 2) {
            return Optional.empty();
        }
        boolean isStatic = strs.length == 3;
        if (isStatic) {
            if (!strs[0].equals("static")) {
                return Optional.empty();
            }
        }
        Optional<Type> type = parserType(strs[isStatic ? 1 : 0]);
        if (type.isEmpty()) {
            return Optional.empty();
        }
        Optional<String[]> identifier = Utils.RegularExpressionGroups(strs[isStatic ? 2 : 1], Identifier.IDENTIFIER_PATTERN);
        if (identifier.isEmpty()) {
            return Optional.empty();
        }
        assert identifier.get().length == 1;
        Optional<Variable> variable = parserVariable(identifier.get()[0]);
        if (variable.isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(new Field(isStatic, type.get(), variable.get(), className));

    }

    //  Parameter = Type Identifier;
    public static Optional<Parameter> parserParameter(String line) {
        String[] strs = line.strip().split(" ");
        if (strs.length != 2) {
            return Optional.empty();
        }
        Optional<Type> type = parserType(strs[0]);
        if (type.isEmpty()) {
            return Optional.empty();
        }
        Optional<String[]> identifier = Utils.RegularExpressionGroups(strs[1], Identifier.IDENTIFIER_PATTERN);
        if (identifier.isEmpty()) {
            return Optional.empty();
        }
        assert identifier.get().length == 1;
        return Optional.of(new Parameter(type.get(), identifier.get()[0]));
    }

    //// 方法调用由方法名和参数构成，保证参数都为identifier不包含复杂表达式 如func(a,b)
    //   callExpr = methodName , "( ", { parameter }, ")";
    public static Optional<CallExpression> parserCallExpression(String line) {
        var matchStr = Utils.RegularExpressionGroups(line.strip(), "(.*?)\\((.*?)\\)");
        if (matchStr.isEmpty()) {
            return Optional.empty();
        }
        var strs = matchStr.get();
        assert strs.length == 3;
        String methodName = strs[1];
        Optional<String> className = Optional.empty();
        // split class name and method name by "."
        String[] methodNameSplit = methodName.split("\\.");
        if (methodNameSplit.length > 2) {
            return Optional.empty();
        }
        if (methodNameSplit.length == 2) {
            className = Optional.of(methodNameSplit[0]);
            methodName = methodNameSplit[1];
        }
        ArrayList<Variables> args = new ArrayList<>();
        if (!strs[2].isEmpty()) {
            for (String s : strs[2].strip().split("[\\s,]+")) {
                Optional<Variables> variable = parserVariables(s);
                if (variable.isEmpty()) {
                    return Optional.empty();
                }
                args.add(variable.get());
            }
        }
        return Optional.of(new CallExpression(className, methodName, args));
    }

    //    // 如 NewInstance(Student)
//    newInstanceExpr = "NewInstance", "(" , className, ")";
    public static Optional<NewInstanceExpression> parserNewInstanceExpression(String line) {
        Optional<String[]> className = Utils.RegularExpressionGroups(line.strip(), String.format("NewInstance\\([\\s]*(%s)[\\s]*\\)", Identifier.IDENTIFIER_PATTERN));
        if (className.isEmpty()) {
            return Optional.empty();
        }
        assert className.get().length == 2;
        return Optional.of(new NewInstanceExpression((new Identifier(className.get()[1]))));
    }

    //    //  如 NewArray(Student, 2, 3)
//    newArrayExpr = "NewArray" , "(", type, dimension, length, ")";
    public static Optional<NewArrayExpression> parserNewArrayExpression(String line) {
        Optional<String[]> arrayInfo = Utils.RegularExpressionGroups(line.strip(), "NewArray\\([\\s]*(.*?)[\\s]*\\)");
        if (arrayInfo.isEmpty()) {
            return Optional.empty();
        }
        assert arrayInfo.get().length == 2;
        var infos = arrayInfo.get()[1].split("[\\s,]+");
        if (infos.length != 3) {
            return Optional.empty();
        }
        Optional<Type> type = parserType(infos[0]);
        if (type.isEmpty()) {
            return Optional.empty();
        }
        // may use variable in length
        Optional<Variables> variables = parserVariables(infos[2]);
        if (variables.isEmpty()) {
            return Optional.empty();
        }
        return type.map(value -> new NewArrayExpression(value, Integer.parseInt(infos[1]), variables.get()));
    }

    //// 定义，包括类型和变量名 如 Student a;
//    declareExpr = [type]， identifier；
    public static Optional<DeclareExpression> parserDeclareExpression(String line) {
        String[] strs = line.strip().split(" ");
        if (strs.length == 2) {
            Optional<Type> type = parserType(strs[0]);
            if (type.isEmpty()) {
                return Optional.empty();
            }
            Optional<Variables> variables = parserVariables(strs[1]);
            return variables.map(value -> new DeclareExpression(type, value));
        } else if (strs.length == 1) {
            Optional<Variables> variables = parserVariables(strs[0]);
            return variables.map(value -> new DeclareExpression(Optional.empty(), value));
        } else {
            return Optional.empty();
        }
    }

    //    // 新建对象， NewInstance或NewArray
//    newStmt = newInstanceExpr | newArrayExpr;
    public static Optional<NewStatement> parserNewStatement(String line) {
        Optional<NewInstanceExpression> newInstanceExpression = parserNewInstanceExpression(line);
        if (newInstanceExpression.isPresent()) {
            return Optional.of(new NewStatement(newInstanceExpression.get()));
        }
        Optional<NewArrayExpression> newArrayExpression = parserNewArrayExpression(line);
        return newArrayExpression.map(NewStatement::new);
    }

    //// 更新操作即AssignStatement 左值可以为声明变量或者声明， 右值可以为新建对象、函数调用和变量， 如refUpdate(Student a, NewInstance(Student))
    //    refUpdateStmt = (variable | declareExpr)  ,  "=" , [ "(", type ,")"], (newInstanceExpr | newArrayExpr | callExpr | variable) ;
    public static Optional<AssignStatement> parserAssignStatement(String line) {
        String[] strs = line.strip().split("=");
        if (strs.length != 2) {
            return Optional.empty();
        }
        Optional<DeclareExpression> declareExpression = parserDeclareExpression(strs[0]);
        if (declareExpression.isEmpty()) {
            return Optional.empty();
        }
        // parse whether it is a cast type
        String regex = "^\\((.*?)\\)(.*?)$";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(strs[1].strip());

        Optional<Type> castType;
        if (matcher.matches()) {
            Optional<Type> t = parserType(matcher.group(1));
            if (t.isEmpty()) {
                return Optional.empty();
            }
            castType = t;
            strs[1] = matcher.group(2).strip();
        } else {
            castType = Optional.empty();
        }
        // remove the outer () if exists
        if (strs[1].startsWith("(") && strs[1].endsWith(")")) {
            strs[1] = strs[1].substring(1, strs[1].length() - 1);
        }
        Optional<NewInstanceExpression> newInstanceExpression = parserNewInstanceExpression(strs[1]);
        if (newInstanceExpression.isPresent()) {
            return Optional.of(new AssignStatement(declareExpression.get(), castType, newInstanceExpression.get()));
        }
        Optional<NewArrayExpression> newArrayExpression = parserNewArrayExpression(strs[1]);
        if (newArrayExpression.isPresent()) {
            return Optional.of(new AssignStatement(declareExpression.get(), castType, newArrayExpression.get()));
        }
        Optional<CallExpression> callExpression = parserCallExpression(strs[1]);
        if (callExpression.isPresent()) {
            return Optional.of(new AssignStatement(declareExpression.get(), castType, callExpression.get()));
        }
        Optional<Variables> variables = parserVariables(strs[1]);
        return variables.map(value -> new AssignStatement(declareExpression.get(), castType, value));
    }

    //// return 保证没有不返回函数调用 如 return a.b;
    //    returnStmt = "return" , variable;
    public static Optional<ReturnStatement> parserReturnStatement(String line) {
        Optional<String[]> strs = Utils.RegularExpressionGroups(line, "return (.*?)");
        if (strs.isEmpty()) {
            return Optional.empty();
        }
        if (strs.get().length != 2) {
            return Optional.empty();
        }
        Optional<Variables> variables = parserVariables(strs.get()[1]);
        return variables.map(ReturnStatement::new);
    }

    //    callStmt = callExpr;
    public static Optional<CallStatement> parserCallStmt(String line) {
        Optional<CallExpression> callExpression = parserCallExpression(line);
        return callExpression.map(CallStatement::new);
    }

    //  spawnExpr = "spawn " + callExpr;
    public static Optional<SpawnExpression> parserSpawnExpr(String line) {
        Optional<String[]> strs = Utils.RegularExpressionGroups(line.strip(), "spawn (.*?)");
        if (strs.isEmpty()) {
            return Optional.empty();
        }
        if (strs.get().length != 2) {
            return Optional.empty();
        }
        Optional<CallExpression> callExpression = parserCallExpression(strs.get()[1]);
        return callExpression.map(SpawnExpression::new);
    }

    // spawnStmt = spawnExpr;
    public static Optional<SpawnStatement> parserSpawnStmt(String line) {
        Optional<SpawnExpression> spawnExpression = parserSpawnExpr(line);
        return spawnExpression.map(SpawnStatement::new);
    }

    //    // 基础语句包括对象更新、函数调用、返回；
//    simpleStmt = refUpdateStmt | returnStmt | callStmt | spawnStmt;
    public static Optional<Statement> parserSimpleStatement(String line) {
        Optional<String[]> strs = Utils.RegularExpressionGroups(line.strip(), "(.*?)\\;");
        if (strs.isEmpty()) {
            return Optional.empty();
        }
        if (strs.get().length != 2) {
            return Optional.empty();
        }
        String str = strs.get()[1];
        Optional<AssignStatement> assignStatement = parserAssignStatement(str);
        if (assignStatement.isPresent()) {
            return Optional.of(assignStatement.get());
        }
        Optional<SpawnStatement> spawnStatement = parserSpawnStmt(str);
        if (spawnStatement.isPresent()) {
            return Optional.of(spawnStatement.get());
        }
        Optional<ReturnStatement> returnStatement = parserReturnStatement(str);
        if (returnStatement.isPresent()) {
            return Optional.of(returnStatement.get());
        }
        Optional<CallStatement> callStmt = parserCallStmt(str);
        return callStmt.map(Statement.class::cast);
    }

    // stmt = loopStmt | simpleStmt;
    public static Optional<Statement> parserStatement(String line, BufferedReader reader) throws Exception {
        Optional<LoopStatement> loopStatement = parserLoopStatement(line, reader);
        if (loopStatement.isPresent()) {
            return Optional.of(loopStatement.get());
        }
        Optional<Statement> simpleStatement = parserSimpleStatement(line);
        if (simpleStatement.isPresent()) {
            return Optional.of(simpleStatement.get());
        }
        System.out.println("fail to parser line: " + line);
        return Optional.empty();
    }

    public static Optional<LoopStatement> parserLoopStatement(String line, BufferedReader reader) throws Exception {
        Optional<String[]> strs = Utils.RegularExpressionGroups(line.strip(), "Loop\\((.*?),(.*?),(.*?),(.*?)\\)[\\s]*\\{");
        if (strs.isEmpty()) {
            return Optional.empty();
        }
        if (strs.get().length != 5) {
            return Optional.empty();
        }
        Optional<Variables> indexVar = parserVariables(strs.get()[1].strip());
        if (indexVar.isEmpty()) {
            return Optional.empty();
        }
        Optional<Variables> startVal = parserVariables(strs.get()[2].strip());
        if (startVal.isEmpty()) {
            return Optional.empty();
        }
        Optional<Variables> endVal = parserVariables(strs.get()[3].strip());
        if (endVal.isEmpty()) {
            return Optional.empty();
        }
        Optional<Variables> stepVal = parserVariables(strs.get()[4].strip());
        if (stepVal.isEmpty()) {
            return Optional.empty();
        }
        ArrayList<Statement> statements = new ArrayList<>();
        boolean useSpawn = false;
        while (true) {
            line = reader.readLine();
            if (line == null) {
                return Optional.empty();
            }
            if (line.strip().equals("}")) {
                break;
            }
            if (line.strip().isEmpty()) {
                continue;
            }
            Optional<Statement> stmt = parserStatement(line, reader);
            if (stmt.isEmpty()) {
                return Optional.empty();
            }
            if (stmt.get() instanceof SpawnStatement) {
                useSpawn = true;
            }
            if (stmt.get() instanceof LoopStatement) {
                if (((LoopStatement) stmt.get()).useSpawn) {
                    useSpawn = true;
                }
            }
            statements.add(stmt.get());
        }
        return Optional.of(new LoopStatement(indexVar.get(), startVal.get(), endVal.get(), stepVal.get(), statements, useSpawn));
    }


    //    ParameterList = Parameter ("," Parameter)*;
//    Parameter = Type Identifier;
//// 函数由返回值、方法名称、 参数类型、参数名称、 函数语句等构成。
//    method = type , methodName, "(", [parameterList], ")" , "{", {stmt, "\n"}, "}";
    public static Optional<Method> parserMethod(String line, BufferedReader reader) throws Exception {
        while (line != null && line.isBlank()) {
            line = reader.readLine();
        }
        if (line == null) {
            return Optional.empty();
        }
        Optional<String[]> strs = Utils.RegularExpressionGroups(line.strip(), "(.*?)\\((.*?)\\)[\\s]*\\{");
        if (strs.isEmpty()) {
            System.out.println("fail to parser line: " + line);
            return Optional.empty();
        }
        if (strs.get().length != 3) {
            System.out.println("fail to parser line: " + line);
            return Optional.empty();
        }
        String typeAndMethodName = strs.get()[1].strip();
        String returnTypeStr = typeAndMethodName.split(" ")[0];
        Optional<Type> returnType = parserType(returnTypeStr);
        if (returnType.isEmpty()) {
            System.out.println("fail to parser line: " + line);
            return Optional.empty();
        }
        String methodNameStr = typeAndMethodName.split(" ")[1];
        Optional<String[]> methodNameRes = Utils.RegularExpressionGroups(methodNameStr, Identifier.IDENTIFIER_PATTERN);
        if (methodNameRes.isEmpty()) {
            System.out.println("fail to parser line: " + line);
            return Optional.empty();
        }
        assert methodNameRes.get().length == 1;
        String methodName = methodNameRes.get()[0];
        ArrayList<Parameter> parameters = new ArrayList<>();
        if (!strs.get()[2].isEmpty()) {
            for (String s : strs.get()[2].strip().split(",")) {
                Optional<Parameter> parameter = parserParameter(s);
                if (parameter.isEmpty()) {
                    System.out.println("fail to parser line: " + line);
                    return Optional.empty();
                }
                parameters.add(parameter.get());
            }
        }
        boolean useSpawn = false;
        ArrayList<Statement> stmts = new ArrayList<>();
        while (true) {
            line = reader.readLine();
            if (line == null) {
                return Optional.empty();
            }
            if (line.strip().equals("}")) {
                break;
            }
            if (line.strip().isEmpty()) {
                continue;
            }
            Optional<Statement> stmt = parserStatement(line, reader);
            if (stmt.isEmpty()) {
                return Optional.empty();
            }
            if (stmt.get() instanceof SpawnStatement) {
                useSpawn = true;
            }
            if (stmt.get() instanceof LoopStatement) {
                if (((LoopStatement) stmt.get()).useSpawn) {
                    useSpawn = true;
                }
            }
            stmts.add(stmt.get());
        }
        return Optional.of(new Method(Optional.empty(), methodName, returnType.get(), stmts.toArray(new Statement[0]), parameters.toArray(new Parameter[0]), useSpawn));
    }

    public static Optional<Klass> parserKlass(BufferedReader reader) throws Exception {
        String line = reader.readLine();
        while (line != null && line.isBlank()) {
            line = reader.readLine();
        }
        if (line == null) {
            return Optional.empty();
        }
        Optional<String[]> strs = Utils.RegularExpressionGroups(line.strip(), "(class|interface)(.*?)\\{");
        if (strs.isEmpty()) {
            System.out.println("fail to parser line: " + line);
            return Optional.empty();
        }
        if (strs.get().length != 3) {
            return Optional.empty();
        }
        boolean isInterface = strs.get()[1].strip().equals("interface");
        String classNameStr = strs.get()[2].strip();
        Optional<String[]> classNameRes = Utils.RegularExpressionGroups(classNameStr, Identifier.IDENTIFIER_PATTERN);
        if (classNameRes.isEmpty()) {
            System.out.println("fail to parser line: " + line);
            return Optional.empty();
        }
        assert classNameRes.get().length == 1;
        String className = classNameRes.get()[0];
        ArrayList<Field> fields = new ArrayList<>();
        ArrayList<Method> methods = new ArrayList<>();
        while (true) {
            line = reader.readLine();
            if (line == null) {
                return Optional.empty();
            }
            if (line.strip().equals("}")) {
                break;
            }
            if (line.strip().isEmpty()) {
                continue;
            }
            Optional<Field> field = parserField(line, className);
            if (field.isPresent()) {
                fields.add(field.get());
                continue;
            }
            //TODO handle later, no need for now
            // may need reader to mark & reset
            Optional<Method> method = parserMethod(line, reader);
            if (method.isEmpty()) {
                return Optional.empty();
            }
            methods.add(method.get());
        }
        if (!isInterface) {
            Type.classSet.add(className);
        }
        return Optional.of(new Klass(className, fields.toArray(new Field[0]), methods.toArray(new Method[0])));
    }
}

